/* Zatok - язык программирования текстовых игр
 * (С) 2010 - 2012 Меркулов Матвей Юрьевич
 * Код лицензирован под лицензией GNU LGPL
 * Вы можете прочитать текст лицензии по адресу:
 * http://www.gnu.org/copyleft/lesser.html
 * или прочитать русский перевод по адресу:
 * http://ru.wikisource.org/wiki/GNU_Lesser_General_Public_License
 */

package parser;

import base.*;
import base.array.*;
import base.context.*;
import base.cycle.*;
import base.function.*;
import base.function.condition.CIf;
import base.function.operator.*;
import base.function.text.CLowerCase;
import base.function.text.CUpperCase;
import base.intrface.CCommandParameter;
import base.list.*;
import base.module.*;
import base.module.intrface.*;
import base.userfunction.*;
import base.value.*;
import base.value.userobject.*;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CParser {
	private static HashMap<String, Class> Keywords = new HashMap<String, Class>();
	private static HashMap<String, Class> Modules = new HashMap<String, Class>();
	public static HashMap<Class, String> ModuleNames = new HashMap<Class, String>();
	private static char[] Separators = { ':', '=', '+', '-', '*', '/', ',', '<', '>', '(', ')', '|', '.', '[', ']' };
	private static String[] KeywordsList = { "иначе", "иначеесли", "конецесли", "конецобъекта", "конецкоманды", "конецфункции", "конецблока", "конеццикла" };
	private static String[] FirstPassKeywordsList = { "названиепроекта", "синонимы", ":", "заменять" };

	private static HashMap<String, String> Synonyms = new HashMap<String, String>();
	public static HashMap<String, CObject> Objects = new HashMap<String, CObject>();
	public static HashMap<String, CGlobalVariable> GlobalVariables = new HashMap<String, CGlobalVariable>();
	public static HashMap<String, CParameter> Parameters = new HashMap<String, CParameter>();
	public static HashMap<String, CUserFunctionID> Functions = new HashMap<String, CUserFunctionID>();
	public static HashMap<Integer, HashMap<Integer, CUserFunction>> UserFunctions = new HashMap<Integer, HashMap<Integer, CUserFunction>>();
	public static CUserFunction CurrentFunction;
	public static CCommand CurrentCommand;
	public static CInheritance CurrentInheritance;
	public static CReader CurrentReader;



	public static void Process( String Filename ) {
		Process1( Filename );
		Process2( Filename );
		CInheritance.ApplyInheritances();
	}


	
	// первый проход
	public static void Process1( String Filename ) {
		CReader OldCurrentReader = CurrentReader;
		CurrentReader = new CReader( Filename );

		int ObjectDepth = 0;
		boolean InsideContextObject = false;
		while ( true ) {
			LinkedList<String> Tokens = ReadTokens( false );
			if ( CurrentReader.EOF ) break;

			String FirstToken = Tokens.getFirst().toLowerCase();

			if ( FirstToken.equals( "названиепроекта" ) ) {
				Tokens.removeFirst();
				CProject.Name = ParseFunction( Tokens ).ToString();
			} else if ( FirstToken.equals( "включить" ) ) {
				Tokens.removeFirst();
				LinkedList<String> NewTokens = new LinkedList<String>();
				for( String Token : Tokens ) {
					NewTokens.add( Token.toLowerCase() );
				}
				Process1( ParseFunction( NewTokens ).ToString() );
			} else if ( FirstToken.equals( "синонимы" ) ) {
				if ( Tokens.size() < 4 ) Error();
				if ( !Tokens.get( 2 ).equals( ":" ) ) Error();
				String BaseToken = Tokens.get( 1 );
				BaseToken = ExtractString( BaseToken ).toLowerCase();

				ListIterator<String> TokensIterator = Tokens.listIterator( 2 );
				while ( TokensIterator.hasNext() ) {
					TokensIterator.next();
					if ( !TokensIterator.hasNext() ) Error();
					
					String Token = TokensIterator.next().toLowerCase();
					Synonyms.put( Token, BaseToken );
				}
			} else if ( FirstToken.equals( ":" ) ) {
				CCase.AddWordCases( Tokens );
			} else if ( FirstToken.equals( "заменять" ) ) {
				if ( Tokens.size() != 4 ) Error();
				if ( !Tokens.get( 2 ).toLowerCase().equals( "на" ) ) Error();
				CReplaceExp.Create( ExtractString( Tokens.get( 1 ).toLowerCase() ), ExtractString( Tokens.getLast().toLowerCase() ) );
			} else if ( FirstToken.equals( "объект" ) ) {
				ObjectDepth++;
				if ( Tokens.size() < 2 ) Error();
				AddUserObject( Tokens.get( 1 ), CUserObject.Create( new CBaseData() ) );
			} else if ( FirstToken.equals( "клонобъекта" ) ) {
				ObjectDepth++;
			} else if ( FirstToken.equals( "конецобъекта" ) || FirstToken.equals( "конецрасположения" ) ) {
				ObjectDepth--;
			} else if ( FirstToken.equals( "команда" ) ) {
				InsideContextObject = true;
			} else if ( FirstToken.equals( "конецфункции" ) || FirstToken.equals( "конецкоманды" ) ) {
				InsideContextObject = false;
			} else if ( Tokens.size() >= 2 )  {
				if ( FirstToken.equals( "функция" ) ) {
					InsideContextObject = true;
					CUserFunction.ParseHeader( Tokens );
				} else {
					String Token = "";
					if ( Tokens.get( 1 ).equals( "=" ) ) Token = Tokens.getFirst();
					if ( FirstToken.equals( "циклдля" ) || FirstToken.equals( "циклдлякаждого" ) ) Token = Tokens.get( 1 );
					if( !Token.isEmpty() ) {
						if( ObjectDepth == 0 && !InsideContextObject ) {
							AddGlobalVariable( Token );
							//System.out.println( Token );
						} else {
							AddParameter( Token );
							//System.out.println( "# " + Token );
						}
					}
				}
			}
			
		}

		CurrentReader.Close();
		CurrentReader = OldCurrentReader;
		//LogCases();
	}



	// второй проход
	public static void Process2( String Filename ) {
		CReader OldCurrentReader = CurrentReader;
		CurrentReader = new CReader( Filename );

		while ( true ) {
			LinkedList<String> Tokens = ReadTokens();
			if ( CurrentReader.EOF ) break;

			CObject.AddModule( CProject.RootObject.Initialization, Tokens );
		}

		CurrentReader.Close();
		CurrentReader = OldCurrentReader;
	}



	public static <E> void AddObject( String Name, E Obj, HashMap<String, E> Map ) {
		for( String DeclinedName : CCase.DeclinedNames( Name, false ) ) {
			Map.put( DeclinedName, Obj );
		}
	}
	

	
	public static void AddObject( String Name, CObject Obj ) {
		AddObject( Name, Obj, Objects );
	}
	


	public static void AddGlobalVariable( String Name ) {
		String ID = RemoveBrackets( Name ).toLowerCase();
		if( Parameters.containsKey( ID ) ) Parameters.remove( ID );
		CGlobalVariable Variable = GlobalVariables.get( ID );
		if( Variable == null ) {
			Variable = new CGlobalVariable();
			Variable.Name = RemoveBrackets( Name );
			AddObject( Name, Variable, GlobalVariables );
		}
	}



	public static void AddParameter( String Name ) {
		String ID = RemoveBrackets( Name ).toLowerCase();
		if( Parameters.containsKey( ID ) ) return;
		CParameter Parameter = Parameters.get( ID );
		if( Parameter == null ) {
			Parameter = new CParameter();
			Parameter.Name = RemoveBrackets( Name );
			AddObject( Name, Parameter, Parameters );
		}
	}



	public static void AddUserObject( String Name, CUserObject Obj ) {
		String ID = RemoveBrackets( Name.toLowerCase() );
		if( Objects.get( ID ) != null ) Error( "Объект с таким именем уже определен" );
		AddObject( Name, Obj, Objects );
		CProject.ObjectsList.add( Obj );
		Obj.BaseData.SetNames( Name );
	}



	public static LinkedList<String> ReadTokens( boolean SecondPass ) {
		Main: while (true) {
			String Line = CurrentReader.ReadLine();
			LinkedList<String> Tokens = ToTokenList( Line, SecondPass );
			if ( CurrentReader.EOF ) return null;

			// пропускаем пустые строки
			if ( Tokens.isEmpty() ) continue;

			if ( SecondPass ) {
				// пропуск строк с ключевыми словами первого прохода
				String FirstToken = Tokens.getFirst();
				for ( String Keyword : FirstPassKeywordsList ) {
					if ( Keyword.equals( FirstToken ) ) continue Main;
				}
			}

			return Tokens;
		}
	}



	public static LinkedList<String> ReadTokens() {
		return ReadTokens( true );
	}



	public static LinkedList<String> ToTokenList( String Line, boolean LowerCase ) {
		LinkedList<String> Tokens = new LinkedList<String>();
		boolean Quotes = false;
		boolean Separator = false;
		boolean PrevSeparator; // является ли предыдущий символ разделителем
		boolean Space = false;
		boolean PrevSpace; // является ли предыдущий символ пробелом
		String Keyword = "";

		Line = Line + " ";
		for ( int N = 0; N < Line.length(); N++ ) {
			PrevSpace = Space;
			PrevSeparator = Separator;
			
			// определение, находится ли текст в кавычках
			if ( Line.charAt( N ) == '\"' ) Quotes = !Quotes;

			if ( Quotes || Line.charAt( N ) == '\"'  ) {
				// текст в кавычках в нижний регистр не переводится
				Keyword = Keyword + Line.substring( N, N + 1 );
				if ( !Quotes ) {
					Tokens.add( Keyword );
					Keyword = "";
				}
			} else {
				// отсечение комментариев
				if ( Line.charAt( N ) == '/' ) {
					if ( N + 1 < Line.length() ) {
						if ( Line.charAt( N + 1 ) == '/' ) break;
					}
				}

				// определение, является ли символ пробелом или табулятором
				if ( Line.charAt( N ) == ' ' || Line.charAt( N ) == '\t' ) {
					Space = true;
				} else {
					Space = false;
				}

				// определение, является ли символ разделителем
				Separator = IsSeparator( Line.charAt( N ) );

				// удаление пробелов / табуляции перед другими пробелами и разделителями
				if ( Space ) {
					if ( PrevSeparator ) {
						Separator = true;
						continue;
					}
					if ( PrevSpace ) continue;
				}

				if ( !Separator && !Space ) {
					if ( !Tokens.isEmpty() ) {
						if ( Character.isDigit( Line.charAt( N ) )  ) {
							if ( Tokens.getLast().equals( "~" ) ) {
								Tokens.removeLast();
								Keyword = "-";
							} else if ( Tokens.getLast().equals( "." ) && Keyword.isEmpty() ) {
								Tokens.removeLast();
								String Token = Tokens.getLast();
								if ( Character.isDigit( Token.charAt( Token.length() - 1 ) ) ) {
									Tokens.removeLast();
									Keyword = Token + ".";
								} else {
									Tokens.add( "." );
								}
							}
						}
					}
					Keyword = Keyword + Line.charAt( N );
				} else {
					// замена ключевых слов на синонимы,
					if ( !Keyword.isEmpty() ) {
						String Synonym = Synonyms.get( Keyword.toLowerCase() );
						if ( Synonym != null && Line.charAt( 0 ) != ':' ) {
							// добавление синонима в список токенов
							Tokens.add( Synonym.toLowerCase() );
						} else {
							// добавление ключевого слова в список токенов
							if ( LowerCase ) {
								Tokens.add( Keyword.toLowerCase() );
							} else {
								Tokens.add( Keyword );
							}
						}
						Keyword = "";
					}
				}

				if ( Line.charAt( N ) == '-' ) {
					// обработка случая с унарным минусом - замена его на тильду
					if ( Tokens.isEmpty() ) {
						Tokens.add( "~" );
					} else {
						if ( !Tokens.getLast().equals( ")" ) && PrevSeparator ) {
							Tokens.add( "~" );
						} else {
							Tokens.add( "-" );
						}
					}
				} else {
					// добавление разделителей в список токенов
					if ( Separator ) Tokens.add( Line.substring( N, N + 1 ) );
				}
			}
		}
		return Tokens;
	}



	public static LinkedList<String> ToTokenList( String Line ) {
		return ToTokenList( Line, true );
	}



	public static boolean IsSeparator( char Char ) {
		for ( char Sep : Separators ) {
			if ( Sep == Char ) {
				return true;
			}
		}
		return false;
	}



	public static boolean IsSeparator( String Line ) {
		if ( Line.charAt( 0 ) == '-' ) {
			if ( Line.length() < 2 ) return true;
			if ( Character.isDigit( Line.charAt( 1 ) ) ) return false;
			return true;
		} else {
			return IsSeparator( Line.charAt( 0 ) );
		}
	}



	public static CObject ParseString( LinkedList<String> Tokens ) {
		if ( Tokens.isEmpty() ) Error();

		// проверка на функцию с определенным возвращаемым значением
		if ( Tokens.size() >= 2 ) {
			if ( Tokens.getFirst().equals( "функция" ) ) {
				CUserFunction UserFunction= new CUserFunction();
				return UserFunction.Parse( Tokens );
			}
		}

		// обработка строк вывода текста
		if ( Tokens.getFirst().charAt( 0 ) == '\"' ) {
			CDisplayText DisplayText = new CDisplayText();
			DisplayText.Param = new CObject[ 1 ];
			if ( Tokens.getLast().equals( "+" ) ) {
				Tokens.removeLast();
				DisplayText.LineBreak = false;
			}
			DisplayText.Param[ 0 ] = ParseFunction( Tokens );
			DisplayText.SetFilePosition();
			return DisplayText;
		}

		// проверка на начало строки с ключевого слова
		for ( Map.Entry<String, Class> MapEntry : Keywords.entrySet() ) {
			if ( Tokens.getFirst().equals( MapEntry.getKey() ) ) {
				CObject Module = null;

				try {
					Module = (CObject) MapEntry.getValue().newInstance();
				} catch (InstantiationException ex) {
					Logger.getLogger(CParser.class.getName()).log(Level.SEVERE, null, ex);
				} catch (IllegalAccessException ex) {
					Logger.getLogger(CParser.class.getName()).log(Level.SEVERE, null, ex);
				}
				
				return Module.Parse( Tokens );
			}
		}

		Tokens = CReplaceExp.SearchAndReplace( Tokens, false );

		if ( Tokens.size() > 1 ) {
			if ( !Tokens.get( 1 ).equals( "(" ) ) {
				Tokens.addLast( ")" );
				String Name = Tokens.getFirst();
				Tokens.removeFirst();
				Tokens.addFirst( "(" );
				Tokens.addFirst( Name );
				//PrintTokens( Tokens );
			}
		} else {
			Tokens.addLast( "(" );
			Tokens.addLast( ")" );
		}

		return ParseFunction( Tokens, 0 );
	}



	public static CObject ParseFunction( LinkedList<String> Tokens, int StartIndex ) {
		if( Tokens.size() <= StartIndex ) Error();
		ListIterator<String> TokensIterator = Tokens.listIterator( StartIndex );
		
		String Name = TokensIterator.next();
		if( Name.equals( "(" ) ) {
			Name = "";
			TokensIterator = Tokens.listIterator( StartIndex );
		} else if ( Name.charAt( 0 ) != '-' ) {
			if ( IsSeparator( Name.charAt( 0 ) ) ) Error();
		}
		

		boolean Brackets = false;
		LinkedList<CObject> ParamList = new LinkedList<CObject>();
		if ( TokensIterator.hasNext() ) {
			String Token = TokensIterator.next();
			if ( Token.equals( "(" ) ) {
				int BracketsQuantity = 0;
				Brackets = true;
				LinkedList<String> ParamTokens = new LinkedList<String>();

				while ( true ) {
					if ( Token.equals( "(" ) ) {
						BracketsQuantity++;
						if ( BracketsQuantity > 1 ) ParamTokens.add( Token );
					} else if ( Token.equals( ")" ) ) {
						if ( BracketsQuantity > 1 ) ParamTokens.add( Token );
						BracketsQuantity--;
						// выходим, если достигнута последняя закрывающая скобка
						if ( BracketsQuantity == 0 ) break;
					} else {
						if ( BracketsQuantity == 1 ) {
							if ( Token.equals( "," ) ) {
								if ( ParamTokens.isEmpty() ) Error();
								ParamList.add( ParseFunction( ParamTokens, 0 ) );
								ParamTokens = new LinkedList<String>();
							} else {
								ParamTokens.add( Token );
							}
						} else {
							ParamTokens.add( Token );
						}
					}
					if( !TokensIterator.hasNext() ) Error( "Нарушен баланс скобок" );
					Token = TokensIterator.next();
				}
				
				if ( !ParamTokens.isEmpty() ) ParamList.add( ParseFunction( ParamTokens, 0 ) );
			}
		}

		if ( Name.isEmpty() ) {
			if ( ParamList.size() != 1 ) Error();
			return ParamList.getFirst();
		}

		if ( !Brackets ) {
			// проверка, является ли строка строковой константой (заключена в кавычки)
			if ( Name.startsWith( "\"" ) && Name.endsWith( "\"" ) ) {
				return new CStringValue( ExtractString( Name ) );
			}

			// проверка, является ли строка числом (начинается с цифры или минуса)
			if ( Name.charAt( 0 ) == '-' || Character.isDigit( Name.charAt( 0 ) ) ) {
				if ( ParamList.size() != 0 ) Error();
				if ( Name.indexOf( '.' ) == -1 ) {
					return new CIntValue( Integer.parseInt( Name ) );
				} else {
					return new CFloatValue( Float.parseFloat( Name ) );
				}
			}
			
			Name = RemoveBrackets( Name );

			// проверка, является ли строка параметром функции
			if ( CurrentFunction != null ) {
				CFunctionParameter Parameter = CurrentFunction.Parameters.get( Name );
				if ( Parameter != null ) return Parameter;
			}

			// проверка, является ли строка идентификатором объекта
			CObject Obj = Objects.get( Name );
			if ( Obj != null ) return Obj;
			
			// проверка, является ли строка идентификатором глобальной переменной
			CGlobalVariable Variable = GlobalVariables.get( Name );
			if ( Variable != null ) return Variable;
			
			// проверка, является ли строка параметром объекта
			CParameter Parameter = Parameters.get( Name );
			if ( Parameter != null ) return Parameter;
			
			Error( "Неизвестный параметр \"" + Name + "\"" );
		} else {
			CModule Module = null;
			
			// проверка, является ли строка названием функции
			CUserFunctionID FunctionID = Functions.get( Name + "&" + ParamList.size() );
			if ( FunctionID != null ) {
				Module = new CFunctionCall( FunctionID );
			} else {
				// проверка, является ли строка названием модуля
				Class FunctionClass = Modules.get( Name );
				if ( FunctionClass == null ) {
					Module = new CFunctionCall( AddUserFunction( null, null, Name, ParamList.size() ) );
				} else {
					try {
						Module = (CModule) FunctionClass.newInstance();
					} catch (InstantiationException ex) {
						Logger.getLogger(CParser.class.getName()).log(Level.SEVERE, null, ex);
					} catch (IllegalAccessException ex) {
						Logger.getLogger(CParser.class.getName()).log(Level.SEVERE, null, ex);
					}
				}
			}
			
			Module.SetFilePosition();
			Module.Param = new CObject[ ParamList.size() ];
			ParamList.toArray( Module.Param );
			Module.Init();
			return Module;
		}
		Error();
		return null;
	}



	public static CObject ParseFunction( LinkedList<String> Tokens ) {
		return ParseFunction( CReplaceExp.SearchAndReplace( Tokens, true ), 0 );
	}



	public static String ExtractString( String Line ) {
		if ( Line.charAt( 0 ) != '"' || Line.charAt( Line.length() - 1 ) != '"' ) Error( "Ожидается строковая константа (текст в кавычках)" );
		return Line.substring( 1, Line.length() - 1 );
	}



	public static void PrintTokens( LinkedList<String> Tokens ) {
		if( Tokens == null ) return;
		String Line = "";
		for ( String Token : Tokens ) {
			Line = Line + Token + " ";
		}
		System.out.println( Line );
	}



	public static void Init() {
		CProject.CurrentObject = CProject.RootObject;
		
		Keywords.put( "объект", CUserObject.class );
		Keywords.put( "клонобъекта", CUserObject.class );
		Keywords.put( "расположение", CUserObject.class );
		Keywords.put( "команда", CCommand.class );
		Keywords.put( "функция", CUserFunction.class );

		Keywords.put( "включить", CInclude.class );
		Keywords.put( "вставить", CInsert.class );

		Keywords.put( "если", CIf.class );
		Keywords.put( "циклдлякаждого", CForEach.class );
		Keywords.put( "циклдля", CFor.class );
		Keywords.put( "циклпока", CWhile.class );

		CReplaceExp.Create( "$ %1 =", "%1 :=" );

		CReplaceExp.Create( "%1 . %20", "Параметр( %1, %2 )" );

		CReplaceExp.Create( "%10 [ %2 ]", "элемент( %1, %2 )" );
		CReplaceExp.Create( "%10 [ %2..%3 ]", "вычленить( %1, %2, %3 )" );
		CReplaceExp.Create( "%10 [ ..%2 ]", "вычленить( %1, 1, %2 )" );
		CReplaceExp.Create( "%10 [ %2.. ]", "вычленить( %1, %2, 0 )" );
		
		CReplaceExp.Create( "~%1", "минус( %1 )" );

		CReplaceExp.Create( "| %1 |", "модуль( %1 )" );

		CReplaceExp.Create( "%1 ... %2", "случайноечисло( %1, %2 )" );
		CReplaceExp.Create( "%1 * %2", "умножить( %1, %2 )" );
		CReplaceExp.Create( "%1 / %2", "разделить( %1, %2 )" );

		CReplaceExp.Create( "%1 + %2", "сложить( %1, %2 )" );
		CReplaceExp.Create( "%1 - %2", "вычесть( %1, %2 )" );

		CReplaceExp.Create( "%1 СодержитПараметр %29 Равный %3", "СодержитПараметр( %1, %2 , %3 )" );
		CReplaceExp.Create( "%1 СодержитПараметр %29", "СодержитПараметр( %1, %2 )" );
		CReplaceExp.Create( "%1 СодержитФункцию %29", "СодержитФункцию( %1, %2 )" );
		CReplaceExp.Create( "%1 СодержитКоманду %29", "СодержитКоманду( %1, %2 )" );
		CReplaceExp.Create( "%1 Содержит %29", "Содержит( %1, %2 ) " );
		
		CReplaceExp.Create( "не %19", "не( %1 )" );
		
		CReplaceExp.Create( "%1 = %2", "равно( %1, %2 )" );
		CReplaceExp.Create( "%1 > %2", "больше( %1, %2 )" );
		CReplaceExp.Create( "%1 < %2", "меньше( %1, %2 )" );
		CReplaceExp.Create( "%1 >= %2", "большеилиравно( %1, %2 )" );
		CReplaceExp.Create( "%1 <= %2", "меньшеилиравно( %1, %2 )" );
		CReplaceExp.Create( "%1 <> %2", "неравно( %1, %2 )" );

		CReplaceExp.Create( "%1 и %2", "и( %1, %2 )" );
		CReplaceExp.Create( "%1 или %2", "или( %1, %2 )" );

		CReplaceExp.Create( "%1 := %2", "приравнять( %1, %2 )" );

		CReplaceExp.Create( "%1 += %2", "ДобавитьК( %1, %2 )" );
		CReplaceExp.Create( "%1 -= %2", "ВычестьИз( %1, %2 )" );
		CReplaceExp.Create( "%1 *= %2", "УмножитьНа( %1, %2 )" );
		CReplaceExp.Create( "%1 /= %2", "РазделитьНа( %1, %2 )" );
		CReplaceExp.Create( "%1++ $", "Инкремент( %1 )" );
		CReplaceExp.Create( "%1-- $", "Декремент( %1 )" );
		
		CReplaceExp.Create( "Название( %1 В %2 )", "Название( %1, %2 )" );
		CReplaceExp.Create( "Название( %1 В %2 %3 )", "Название( %1, %2, %3 )" );
		CReplaceExp.Create( "ОстатокОтДеления ( %1 На %2 )", "ОстатокОтДеления( %1, %2 )" );
		CReplaceExp.Create(  "Разместить %1 Перед %2", "РазместитьПеред( %1, %2 )" );
		CReplaceExp.Create( "Разместить %1 После %2", "РазместитьПосле( %1, %2 )" );
		CReplaceExp.Create( "Переместить %1 В %2", "Переместить( %1, %2 )" );
		
		AttachModule( "Модуль", CModule.class );

		AttachModule( "Приравнять", CEquate.class );
		AttachModule( "Параметр", CGetParameter.class );
		AttachModule( "Вернуть", CReturn.class );
		
		AttachModule( "Название", CName.class );
		AttachModule( "Номер", CNumber.class );
		AttachModule( "Однотипны", COfSameType.class );
		AttachModule( "Идентичны", CIdentical.class );
		AttachModule( "Клонировать", CCloneObject.class );
		AttachModule( "СодержитПараметр", CContainsParameter.class );
		AttachModule( "СодержитФункцию", CContainsFunction.class );
		AttachModule( "СодержитКоманду", CContainsCommand.class );

		AttachModule( "Сложить", CAdd.class );
		AttachModule( "Вычесть", CSubtract.class );
		AttachModule( "Умножить", CMultiply.class );
		AttachModule( "Разделить", CDivide.class );
		AttachModule( "ДобавитьК", CAddTo.class );
		AttachModule( "ВычестьИз", CSubtractFrom.class );
		AttachModule( "УмножитьНа", CMultiplyBy.class );
		AttachModule( "РазделитьНа", CDivideBy.class );
		AttachModule( "Инкремент", CIncrement.class );
		AttachModule( "Декремент", CDecrement.class );
		AttachModule( "ОстатокОтДеления", CModulus.class );
		AttachModule( "СлучайноеЧисло", CRandom.class );

		AttachModule( "Не", CNot.class );
		AttachModule( "Минус", CNegative.class );
		AttachModule( "И", CAnd.class );
		AttachModule( "Или", COr.class );
		AttachModule( "Равно", CEqual.class );
		AttachModule( "Больше", CMore.class );
		AttachModule( "Меньше", CLess.class );
		AttachModule( "БольшеИлиРавно", CMoreOrEqual.class );
		AttachModule( "МеньшеИлиРавно", CLessOrEqual.class );
		AttachModule( "НеРавно", CNotEqual.class );

		AttachModule( "Синус", CSine.class );
		AttachModule( "Косинус", CCosine.class );
		AttachModule( "Модуль", CAbsolute.class );
		AttachModule( "ОкруглитьВверх", CCeiling.class );
		AttachModule( "ОкруглитьВниз", CFloor.class );
		AttachModule( "КвадратныйКорень", CSquareRoot.class );

		AttachModule( "Целое", CIntFunction.class );
		AttachModule( "Дробное", CFloatFunction.class );
		AttachModule( "Строковое", CStringFunction.class );
		AttachModule( "ТипЗначения", CValueType.class );

		AttachModule( "ВерхнийРегистр", CUpperCase.class );
		AttachModule( "НижнийРегистр", CLowerCase.class );

		AttachModule( "Выбор", CSetChoice.class );
		AttachModule( "ОбработатьКоманды", CProcessCommands.class );
		AttachModule( "ПараметрКоманды", CCommandParameter.class );

		AttachModule( "РодительскийОбъект", CParent.class );
		AttachModule( "Переместить", CMoveObject.class );
		AttachModule( "ПереместитьТекущийОбъектВ", CMoveCurrentObjectTo.class );
		AttachModule( "УдалитьТекущийОбъект", CRemoveCurrentObject.class );
		AttachModule( "Удалить", CRemoveObject.class );

		AttachModule( "Список", CCreateList.class );
		AttachModule( "ПервыйОбъект", CFirstObject.class );
		AttachModule( "ПоследнийОбъект", CLastObject.class );
		AttachModule( "Содержит", CContains.class );
		AttachModule( "КоличествоОбъектов", CObjectsQuantity.class );
		AttachModule( "РазместитьПеред", CInsertBefore.class );
		AttachModule( "РазместитьПосле", CInsertAfter.class );
	
		AttachModule( "Массив", CCreateArray.class );
		AttachModule( "ПустойМассив", CCreateEmptyArray.class );
		AttachModule( "Вычленить", CExtract.class );
		AttachModule( "Элемент", CElement.class );
		
		AttachModule( "ЗадатьРазмерыОкна", CSetWindowSize.class );
		AttachModule( "ЗадатьГраницыРазмеровОкна", CSetWindowSizeLimits.class );
		AttachModule( "ЗадатьРамку", CSetFrame.class );
		AttachModule( "ЗадатьОтступыДляТекста", CSetMargins.class );
		AttachModule( "ЗадатьТекстуруФона", CSetTexture.class );
		AttachModule( "ЗадатьРамкуМеню", CSetMenuFrame.class );
		AttachModule( "ЗадатьОтступыДляМеню", CSetMenuMargins.class );
		AttachModule( "ЗадатьТекстуруМеню", CSetMenuTexture.class );
		AttachModule( "ЗадатьЦветФона", CSetBackgroundColor.class );
		AttachModule( "ЗадатьЦветТекста", CSetTextColor.class );
		AttachModule( "ЗадатьЦветСсылки", CSetLinkColor.class );
		AttachModule( "ЗадатьЦветВыбраннойСсылки", CSetSelectedLinkColor.class );
		AttachModule( "ЗадатьШрифт", CSetTextFont.class );
		AttachModule( "ЗадатьШрифтМеню", CSetMenuFont.class );
		AttachModule( "ЗадатьПолосыПрокрутки", CSetScrollBars.class );
		AttachModule( "ЗадатьРазделитель", CSetDivider.class );
						
		AttachModule( "ЗадатьОкноВывода", CSetOutputWindow.class );
		AttachModule( "ОчиститьОкноВывода", CClearOutputWindow.class );

		AttachModule( "БазовоеИзображение", CSetBaseImage.class );
		AttachModule( "РисоватьИзображение", CDrawImage.class );
		AttachModule( "ОтобразитьИзображения", CUpdateImages.class );

		AttachModule( "Прервать", CBreak.class );
		AttachModule( "Продолжить", CContinue.class );
		AttachModule( "Конец", CEnd.class );
		AttachModule( "Ошибка", CError.class );
		AttachModule( "Стоп", CStop.class );
		AttachModule( "Журнал", CWriteLog.class );
		
		AttachModule( "ВыбратьЗначение", CSelectNumber.class );

		CCase.Init();
		
		AddObject( "Время{Проекта}", new CProjectTime() );
		AddObject( "Ширина{Экрана}", new CScreenWidth() );
		AddObject( "Высота{Экрана}", new CScreenHeight() );
		AddObject( "{Да}", new CYes() );
		AddObject( "{Нет}", CProject.No );
		AddObject( "Сам", new CThis() );
	}



	public static void AttachModule( String ModuleName, Class ModuleClass ) {
		Modules.put( ModuleName.toLowerCase(), ModuleClass );
		ModuleNames.put( ModuleClass, ModuleName );
	}



	public static void SearchForIllegalKeywords( LinkedList<String> Tokens, String ExpectedKeywords ) {
		for ( String Keyword : KeywordsList ) {
			if ( Tokens.getFirst().equals( Keyword ) ) Error( "Встречено ключевое слово \"" + Keyword + "\", ожидается \"" + ExpectedKeywords + "\"" );
		}
	}



	public static void Error( String Message ){
 		CGraphics.Error( Message + " в строке " + CurrentReader.CurrentLineNumber + " файла \"" + CurrentReader.GetFilename() + "\"" );
	}



	public static void Error(){
		Error( "Синтаксическая ошибка" );
	}



	public static void Clear() {
		Synonyms.clear();
		Objects.clear();
		CCase.Clear();
		CProject.CurrentObject = null;
		
		CParser.InitObjects();
}



	public static void InitObjects() {
		CProject.RootObject = CUserObject.Create( new CBaseData() );
		CProject.CurrentObject = CProject.RootObject;
		
		ProcessAndExecute( "libs/", "base.zat" );
		ProcessAndExecute( "design/" + CProject.DesignName + "/", "design.zat" );
		CProject.UpdateDesign();

		AddUserObject( "КорневойОбъект", CProject.RootObject );
		AddUserObject( "Ничто", CProject.Null );
	}



	public static String RemoveBrackets( String Name ) {
		return Name.replaceAll( "[\\{\\}]", "" );
	}



	public static CUserObject GetObject( String ID ) {
		CObject Object = CParser.Objects.get( CParser.RemoveBrackets( ID ) );
		if ( Object == null ) Error( "Объект не найден" );
		if ( Object.getClass() != CUserObject.class ) Error( "Объект неверного типа" );
		return ( CUserObject ) Object;
	}

	
	
	public static CUserFunctionID AddUserFunction( CUserObject Object, CUserFunction Function, String Name, int ParamQuantity ) {
		String FullName = Name + "&" + ParamQuantity;
		CUserFunctionID ID = Functions.get( FullName );
		if( ID == null ) ID =  new CUserFunctionID( FullName );
		Functions.put( FullName, ID );
		if( Object != null ) Object.BaseData.Functions.put( ID, Function );
		return ID;
	}
	
	

	public static void ProcessAndExecute( String Dir, String Filename ) {
		String OldProjectDir = CProject.ProjectDir;
		CProject.ProjectDir = Dir;
		
		CParser.Process( Dir + Filename );
		CProject.RootObject.Init();
		CProject.RootObject.Initialization.clear();
		
		CProject.ProjectDir = OldProjectDir;
	}

	
	
	public static void LogCases() {
		PrintWriter PWriter = null;
		try {
			PWriter = new PrintWriter( new FileWriter( "names.csv" ) );
		} catch ( IOException ex ) {
			Logger.getLogger( CProject.class.getName() ).log( Level.SEVERE, null, ex );
		}
		for( String VarName : GlobalVariables.keySet() ) {
			PWriter.println( VarName );
		}
		for( CUserObject Object: CProject.ObjectsList ) {
			String Line = "";
			for( int N = 0; N < 12; N++ ) {
				if( !Line.isEmpty() ) Line += ",";
				Line += Object.BaseData.Name[ N ];
			}
			PWriter.println( Line );
		}
		PWriter.close();
	}
}
